interface A {
    a:number,
    b:string
}
let obj:A={
    a:123,b:'456'
}
let  num:Number
num=500
console.log(num);
let msg:String='晦气'

let flag:Boolean=true
flag=false


let object:Object={
    name:'1',
    age:10
}

// 下面是错误案例
let n:number;
for (let i = 0; i<10;i++) {
    if(i<=5)n=10
    // 这里回报错，因为不能给n赋值为string类型
    // else n='100'
}
// 可以将其改为联合类型或者直接将其标记为any
let n1:any;
// 或者 
// let n1:String|Number
for (let i = 0; i<10;i++) {
    if(i<=5)n1=10
    // 这里报会错，因为不能给n赋值为string类型
    else n1='100'
}
// 枚举 
// 枚举无法修改，因为其是只读属性

// 按照枚举成员分类
// 都有三种 数字，字符串，异构 三种
// 1 数字枚举
enum numTest{
    one,
    two,
    three,
    four
}
// 数字枚举的默认值是从0 开始
console.log(numTest.one,'num'); //0
console.log(numTest[0]);//num

enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
 
console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true
/**
 * 可以看出
 * 1. 数字枚举如果没有初始化，默认初始化值为0，每项+1
 * 2.如果有初始化，则在初始化的基础上，每项+1
 * 3.如果某项被赋值(可以是正数或负数,或者小数)，那么之后的项在此基础上加一
 * 4.如果未动手的赋值的枚举项与手动赋值的重复了，ts是不会察觉的但应该尽量避免
 * 5.反向映射 除了创建一个属性名作为对象成员之外，数字枚举成员(字符串成员没有反向映射
 * )还具有反向映射，从枚举值到枚举名字
 *  ***/

// 字符串枚举
// 在一个字符串枚举里，每个成员都必须用字符串字面量，或另外一个字符串枚举成员进行初始化

enum strTest {
    one='one',
    two='two',
    three='three',
    four='four'
}

// 异构枚举
// 将数字枚举与字符串枚举混用，但不建议

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",
}

// 按照声明方式
// 普通枚举，const枚举，外部枚举，外部常量枚举
// 1.普通枚举

enum normal{
    one,
    two,
    three='three'.length
}
console.log(normal.three); //5
// 下面的情况不被允许
// enum normal{
//     one='one'.length,
//     two,
//     three
// }
// 上面代码会报错，因为one 为 计算值，而后面的值无法获取其数值
// 官方定义，不带初始化的枚举要么被放在第一的位置，要么被放在了使用 了数字常量
// 或者其它常量初始化了的枚举后面

// 2.常数枚举(const enum 定义的枚举)
const enum constTest {
    one,
    two,
    three
}
let constArr=[constTest.one,constTest.two,constTest.three] //[0,1,2]

// 其与普通枚举的区别为，他会在编译阶段被删除，并且不能包括计算成员，加入包括了 会报错

// 3.外部枚举
// 外部枚举的声名方式比较特别，该类型不会生成反向映射
// 外部枚举用来描述已经存在的枚举类型的形状，这样听起来很晦涩。
// 同时可以防止声名枚举的命名冲突和成员冲突

// declare enum next{
//     one=1,
//     two,
//     three,
//     four,
//     five
// }
// console.log(next);
// console.log(next.five);
// 放在另一个文件这里不做赘述
// const next={
//     one:1,
//     two:2,
//     three:3,
//     four:4,
//     five:5
// }

// 4.外部常量枚举
// 和上着没有什么区别，只是会提示是否有枚举命名冲突和成员冲突，不会生成反向映射

let test1:number=2
// ts 中小写字母开头的类型表示字面量大写别欧式的通过new实例化的数据


// 泛型
//  泛型变量 可以表示任意类型
// 在函数名后面用'<>'声名一个泛型变量
function convert<T>(input:T):T {
    return input
}

// covert 中参数我们标记为类型T，返回值也标记为T  //及参数和返回值类型一致
// 即函数的输入和输出的类型一致，这样使用了泛型变量的函数兼做泛型函数

// 泛型类
class Person<U> {
    who:U;
    constructor(who:U) {
        this.who=who
    }
    say(code:U):string{
        return this.who+': i am'+code 
    }
}

// let a =  new Person('晦气'); //等价于下面
let a=new Person<string>('晦气') // 在这里 设置为string
// a.say(007) //错误 参数应该为string
console.log(a.say('007'));//晦气: i am007

/**
 * 传入的泛型变量为 string ,告诉ts这个类的U是string类型, 
 * 通过Person的定义, 我们知道say方法的参数也是string类型, 
 * 所以a.say(007)会报错, 因为007是number. 
 * 多以我们可以通过传入泛型变量来约束泛型**/

// 自动推断泛型变量的类型
// let a =  new Person('詹姆斯邦德');
// 等价 let a =  new Person<string>('詹姆斯邦德');

// 泛型方法
// 其实方法和函数定义方式一样
class ABC {
    // 输入t[],返回T
    getFirst<T>(data:T[]):T{
        return data[0]
    }
}
let A=new ABC()

console.log(A.getFirst([1,2,3]));


// 什么叫做泛型
// 他是一种描述多种类型(类型范围)的格式
// 用动态的类型/泛型变量描述函数和类的方法

// 泛型类型
/**
 * 可以用泛型变量去描述一个类型(类型范围)，ts的数组类型Array本身就是一个
 * 泛型类型
 * **/
let arr:Array<number>;
arr=[123]

function cove<T>(input:T) {
    return input;
}

// 定义泛型类型
interface cove1{
    <T>(input:T):T
}
// 验证两者是否相同

let cove2:cove1=cove //定义一个 cove2 函数 通过接口 cove1 方法为cove

console.log(cove2,'cove2');

// 泛型接口
interface Goods<T>{
    id:number;
    title: string;
    size: T;
}

let apple:Goods<string> = {id:1,title: '苹果', size: 'large'};
let shoes:Goods<number> = {id:1,title: '苹果', size: 43};

// 泛型约束
function echo<T>(input: T): T {
    // console.log(input.name); // 报错, T上不确定是否由name属性
    return input;
}
/**
 * 前面说过T可以代表任意类型, 但对应的都是基础类型, 所以当我们操作input.name的时候就需要标记input上有name属性,
 * 这样就相当于我们缩小了泛型变量的范围, 对泛型进行了约束**/
// 现在T是个有name属性的类型
function echo1<T extends {name:string}>(input: T): T {
    console.log(input.name); // 正确
    return input;
}

// 泛型的应用，工厂函数
function create<T,U>(O: {new(): T|U; }): T|U {
    return new O();
}

// 可以定义多个泛型变量.
// 泛型变量和普通类型用法一直, 也支持联合类型/交叉类型等类型.
// 如果一个数据是可以实例化的, 我们可以用{new(): any}表示.